package week13.week_13_client;

import java.awt.*;
import java.io.*;
import java.net.*;
import java.rmi.server.ExportException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import com.sun.javaws.jnl.IconDesc;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.FileChooser;
import javafx.stage.WindowEvent;
import sun.awt.image.GifImageDecoder;
import sun.awt.windows.ThemeReader;
import sun.reflect.annotation.ExceptionProxy;
import week13.week_13_server.*;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.swing.*;

public class LoginGUI extends Application {
    TextField tf_msg;
    TextArea ta_info;
    DatagramSocket socket = null;
    ArrayList onlineUser=new ArrayList();
    SSLSocketFactory factory=null;
    ObservableList observableList;
    ListView<String> listView;
    Socket s;
    User user;
    Button btn_sendFile;
    FileChooser fileChooser = new FileChooser();
    Button btn_listFile;
    Button btn_disconnect;
    Button btn_editor;
    TextArea ta_editor;

    public LoginGUI(Socket s,SSLSocketFactory factory,User user) throws Exception{
        this.user=user;
        this.s = s;
        this.factory = factory;
        init();
        if(connect())
            start(new Stage());
        else {
            MyUtils.showAlert("连接服务器出错！");
        }
    }

    @Override
    public void start(Stage stage) {
        // TODO Auto-generated method stub
        GridPane pane_com = new GridPane();
        Label lb_msg = new Label("Message:");
        tf_msg = new TextField();
        tf_msg.setPrefColumnCount(20);
        Button btn_send = new Button("   Send  ");
        btn_sendFile = new Button("  发送文件  ");
        //绑定事件从listview获取用户选择的信息传入sendFile函数
        btn_sendFile.setOnAction(event -> {
            ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
            for(Object o : selectedIndices){
                sendFile(((String)o).split(" ")[0]);
            }
        });
        //
        btn_send.setOnAction(event -> {
            ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
            for(Object o : selectedIndices){
                sentTo(((String)o).split(" ")[0]);
            }
        });
        btn_listFile = new Button("  接收文件  ");
        btn_listFile.setOnAction(this::listFile);
        btn_editor = new Button("在线编辑");
        btn_editor.setOnAction(this::onlineEdit);

        Hyperlink hp = new Hyperlink("腾讯新闻");
        hp.setOnAction((ActionEvent e)->{
            try {
                Desktop.getDesktop().browse(new URI("https://news.qq.com/"));
            } catch (IOException ioException) {
                ioException.printStackTrace();
            } catch (URISyntaxException uriSyntaxException) {
                uriSyntaxException.printStackTrace();
            }
        });

        pane_com.addRow(1,hp);
        pane_com.addRow(2, lb_msg, tf_msg, btn_send);
        pane_com.addRow(3,btn_sendFile,btn_listFile,btn_editor);
        GridPane.setHalignment(hp,HPos.CENTER);
        GridPane.setHalignment(btn_sendFile,HPos.RIGHT);
        GridPane.setHalignment(btn_listFile,HPos.CENTER);
        GridPane.setHalignment(btn_editor,HPos.LEFT);
        GridPane.setHalignment(lb_msg,HPos.RIGHT);
        pane_com.setAlignment(Pos.CENTER);
        pane_com.setHgap(20);
        pane_com.setVgap(10);
        pane_com.setMinSize(200, 60);

        TitledPane pane_01 = new TitledPane("聊天窗口(当前用户:"+user.getUserName()+")",pane_com);
        pane_01.setCollapsible(false);

        ta_info = new TextArea();
        ta_info.setMinSize(500, 390);
        ScrollPane pane_info = new ScrollPane(ta_info);

        GridPane pane_02 = new GridPane();
        pane_02.add(pane_info,0,0);
        pane_02.add(listView,1,0);

        VBox box = new VBox(pane_02,pane_01);
        box.setSpacing(10);
        box.setAlignment(Pos.CENTER);

        Scene scene = new Scene(box);
        stage.setScene(scene);
        stage.setTitle("在线用户列表，来自1707");
        stage.setOnCloseRequest(event -> {});
        stage.show();
    }

    public void save(ActionEvent ac){
        System.out.println("save");
        File file = fileChooser.showSaveDialog(new Stage());
        try(
                FileWriter out = new FileWriter(file);
                BufferedWriter bw=new BufferedWriter(out);
        ){
            bw.write(ta_editor.getText());
            bw.flush();
        }catch (IOException ex){
            ex.printStackTrace();
        }
    }

    public void onlineEdit(ActionEvent a){
        GridPane gpane = new GridPane();
        ta_editor = new TextArea();
        Button bt_saveFile = new Button("  保存  ");
        bt_saveFile.setOnAction(this::save);
        GridPane.setHalignment(bt_saveFile,HPos.CENTER);
        gpane.addRow(1,ta_editor);
        gpane.addRow(2,bt_saveFile);
        Scene scene = new Scene(gpane);
        Stage stage = new Stage();
        stage.setScene(scene);
        stage.show();

        new Thread(()->{
            try {
                editor();
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }).start();
    }

    public void editor() throws IOException {
        Listener listener = new Listener();
        Message msg = new Message();
        msg.setMsgType(MessageType.MSG_EDITOR);
        while (true) {
            if (listener.isChange(ta_editor.getText())) {
                msg.setContent(ta_editor.getText());
                listener.sentToServer((SSLSocket) factory.createSocket(ClientInfo.serverAddr, ClientInfo.serverPort),msg);
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    //初始化ListView，启动监听线程
    public void init(){
        observableList = FXCollections.observableList(onlineUser);
        listView = new ListView<>(observableList);
        listView.setItems(observableList);
        new Thread(()->{l();}).start();
    }
    //下载文件窗口
    public void showFileDownStage(ArrayList arr){
        GridPane pane_com = new GridPane();
        Button btn_down = new Button("  下载  ");
        Button btn_showImage = new Button("  预览图片  ");

        ObservableList oblist = FXCollections.observableList(arr);
        ListView lv = new ListView<>(oblist);
        lv.setItems(oblist);

        btn_showImage.setOnAction(event -> {
            ObservableList selectedIndices = lv.getSelectionModel().getSelectedItems();
            for (Object o: selectedIndices){
                showImage(o.toString());
            }
        });

        btn_down.setOnAction(event -> {
            ObservableList selectedIndices = lv.getSelectionModel().getSelectedItems();
            for(Object o : selectedIndices){
                getFile(o.toString());
            }
        });
        GridPane gpane = new GridPane();
        gpane.addRow(1,btn_down,btn_showImage);
        gpane.setHgap(20);
//        GridPane.setHalignment(btn_down,HPos.CENTER);
        GridPane.setHalignment(btn_showImage,HPos.CENTER);
        pane_com.addRow(1,lv);
        pane_com.addRow(2,gpane);

        VBox box = new VBox(pane_com);
        box.setSpacing(10);
        box.setAlignment(Pos.CENTER);
        Scene scene = new Scene(box);
        Stage stage = new Stage();
        stage.setScene(scene);
        stage.setTitle("在线用户列表，来自1707");
        stage.show();
    }
    //下载图片
    public File downImage(String imageName){
        File f = null;
        String suffix = imageName.split("\\.")[1];
        List<String> arr = Arrays.asList("jpg","bmp","gif","png","jpeg");
        if (!arr.contains(suffix)){
            MyUtils.showAlert("只能预览图片");
            return null;
        }
        try (
                SSLSocket s = (SSLSocket) factory.createSocket(ClientInfo.serverAddr, ClientInfo.serverPort);
                ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
                ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
        ){
            f = File.createTempFile("tmp", "."+suffix, new File("D:/tmp/"));
            Message msg = new Message();
            msg.setFromUser(user.getUserName());
            msg.setMsgType(MessageType.MSG_GET_FILE);
            msg.setContent(imageName);
            oos.writeObject(msg);

            DataInputStream dis = new DataInputStream(s.getInputStream());
            FileOutputStream fos = new FileOutputStream(f);
            byte[] buffer = new byte[1024];
            int len = dis.read(buffer);
            while (len != -1){
                fos.write(buffer,0,len);
                fos.flush();
                len = dis.read(buffer);
            }
            fos.close();
            return f;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    //展示图窗口，当退出时删除下载的临时文件
    public void showImage(String imageName){
        File f = downImage(imageName);
        if (f==null) return;
        System.out.println(f.getPath());
        BorderPane root = new BorderPane();
        Stage ImageStage = new Stage();
        Image image = new Image(f.toURI().toString());
        ImageView imageView = new ImageView();
        imageView.setImage(image);
        root.setCenter(imageView);
        double width = image.getWidth();
        double height = image.getHeight();
        Scene scene = new Scene(root,width,height);
        ImageStage.setScene(scene);
        //点击退出事件时删除文件
        ImageStage.setOnCloseRequest((WindowEvent event1) -> {
            f.delete();
        });
        ImageStage.show();
    }
    //通红文件名从服务器获取文件
    public void getFile(String filename){
        try (
                SSLSocket s = (SSLSocket) factory.createSocket(ClientInfo.serverAddr, ClientInfo.serverPort);
                ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
                ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
        ) {
            Message msg = new Message();
            msg.setFromUser(user.getUserName());
            msg.setMsgType(MessageType.MSG_GET_FILE);
            msg.setContent(filename);
            oos.writeObject(msg);

            DataInputStream dis = new DataInputStream(s.getInputStream());
            File file = fileChooser.showSaveDialog(new Stage());
            FileOutputStream fos = new FileOutputStream(file);
            byte[] buffer = new byte[1024];
            int len = dis.read(buffer);
            while (len != -1){
                fos.write(buffer,0,len);
                fos.flush();
                len = dis.read(buffer);
            }
            fos.close();
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
    //获取当前用户的文件列表
    public void listFile(ActionEvent event){
        try (
                SSLSocket s = (SSLSocket) factory.createSocket(ClientInfo.serverAddr, ClientInfo.serverPort);
                ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
                ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
        ){
            Message msg = new Message();
            msg.setMsgType(MessageType.MSG_LIST_FILE);
            msg.setFromUser(user.getUserName());
            oos.writeObject(msg);
            oos.flush();
            msg = (Message) ois.readObject();
            ArrayList<String> arr = (ArrayList) msg.getContent();
            for (String name:arr){
                System.out.println(name+"\n");
            }
            showFileDownStage(arr);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
    //发送文件
    public void sendFile(String username){
        File file = fileChooser.showOpenDialog(new Stage());
        try (
                //创建SSL socket
                SSLSocket s = (SSLSocket) factory.createSocket(ClientInfo.serverAddr, ClientInfo.serverPort);
                //注意：先创建输出流对象，再创建输入流对象，服务器则相反
                ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
                DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        ){
            Message msg = new Message();
            msg.setToUser(username);
            msg.setFromUser(user.getUserName());
            msg.setMsgType(MessageType.MSG_PUT_FILE);
            msg.setContent(file.getName());
            oos.writeObject(msg);
            OutputStream fos =  s.getOutputStream();
            FileInputStream fis = new FileInputStream(file);
            byte[] buffer = new byte[1024];
            int length;
            while((length = fis.read(buffer, 0, buffer.length)) != -1) {
                dos.write(buffer, 0, length);
                dos.flush();
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
    //发送消息给指定用户
    public void sentTo(String username){
        try (
                //创建SSL socket
                SSLSocket s = (SSLSocket) factory.createSocket(ClientInfo.serverAddr, ClientInfo.serverPort);
                //注意：先创建输出流对象，再创建输入流对象，服务器则相反
                ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
                ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
        ){
            Message msg = new Message();
            msg.setToUser(username);
            msg.setFromUser(user.getUserName());
            msg.setMsgType(MessageType.MSG_TRAN);
            ta_info.appendText("我->"+username+":\n"+tf_msg.getText()+"\n");
            msg.setContent(user.getUserName()+":\n"+tf_msg.getText());
            oos.writeObject(msg);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
    //客户端监听socket，接收服务端发来的请求
    public void l(){
        while (true){
            if (s.isClosed()) break;
            try{
                ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
                Message msg = (Message) ois.readObject();
                //接收服务端发来的信息,显示在页面上
                if (msg.getMsgType()==MessageType.MSG_TRAN) {
                    if(msg.getContent() instanceof String) {
                        String str = (String) msg.getContent();
                        if (!msg.getFromUser().equals(user.getUserName())) ta_info.appendText(str + "\n");
                    }
                }
                //
                if (msg.getMsgType()==MessageType.MSG_PUT_FILE){
                    ta_info.appendText(msg.getFromUser()+":\n"+"收到"+msg.getFromUser()+"传输的文件，请在接收窗口接收\n");
                }
                //更新在线用户列表
                if (msg.getMsgType()==MessageType.MSG_FLUSH){
                    onlineUser = (ArrayList) msg.getContent();
                    Platform.runLater(()->{
                        observableList = FXCollections.observableList(onlineUser);
                        listView.setItems(observableList);
                    });
                }
                if (msg.getMsgType() == MessageType.MSG_EDITOR){
                    ta_editor.clear();
                    ta_editor.appendText((String) msg.getContent());
                }
            }catch (Exception ex){
//                ex.printStackTrace();
            }
        }
    }

    public boolean connect (){
        try {
            socket = new DatagramSocket();
            SocketAddress endpoint = new InetSocketAddress(ClientInfo.serverAddr,ClientInfo.serverDataPort);
            socket.connect(endpoint);
            return true;
        } catch (IOException ex) {
            System.out.println("异常信息："+ex.getMessage());
            return false;
        }
    }
    public void btnSendHandler(ActionEvent event){
        String msg_sent = tf_msg.getText();

        try {
            DatagramPacket pout  = new DatagramPacket(msg_sent.getBytes(),msg_sent.getBytes().length);
            socket.send(pout);

            System.out.println(MyUtils.getCurrentTimeString() + ",向服务器发送字符串:"+msg_sent);
            ta_info.appendText(MyUtils.getCurrentTimeString() + ",发送信息给服务器："+msg_sent+"\n");

            DatagramPacket pin = new DatagramPacket(new byte[512],512);
            socket.receive(pin);

            //socket收到的数据的长度不一定是pin包定义的数组的大小的，可以通过pin.getLength()取得接收到的数据的长度。
            //将接收到的数据转换成字符串时，可以设定从包里取多少数据，如下：
            String msg_recv=new String(pin.getData(),0, pin.getLength());

            System.out.println(MyUtils.getCurrentTimeString() + ",从服务器接收回送字符串:"+msg_recv);
            ta_info.appendText(MyUtils.getCurrentTimeString() + ",从服务器接收信息："+msg_recv+"\n");

        } catch (IOException ex) {
            System.out.println("异常信息："+ex.getMessage());
            MyUtils.showAlert("连接服务器或发送信息出错！");
        }
    }
}
